package net.howson.phil.datahiding.old;

import java.util.Random;

public class DefaultEncodingScheme2 implements EncodingScheme {

	private final Random random;
	private final Random random2;

	private static final boolean[][] combinations = new boolean[][] {
			{ true, true, false },
			{ true, false, true },
			{ false, true, true } };

	public DefaultEncodingScheme2(int randomSeed) {
		this.random = new Random(randomSeed);
		this.random2 = new Random();
	}

	@Override
	public int decode(int red, int green, int blue, int seedRed, int seedGreen, int seedBlue) {
		if (seedRed<0) {
			throw new IllegalArgumentException("Require a seed file for extraction with this encoding scheme");
		}
		
		int comb = random.nextInt(combinations.length);
		boolean[] b = combinations[comb];
		int[] nibbles = new int[2];
		int i = 0;
		if (b[0]) {
			nibbles[i++] = getVal(red, seedRed);
		}
		if (b[1]) {
			nibbles[i++] = getVal(green, seedGreen);
		}
		if (b[2]) {
			nibbles[i++] = getVal(blue, seedBlue);
		}

		int recoveredValue = (((nibbles[0] << 4) | nibbles[1]) & 0xFF) ^ (random.nextInt(256)-128);
		return recoveredValue;
	}

	private int getVal(int x, int y) {
		if (x<y) {
			return y-x;
		}
		return x-y;
	}

	@Override
	public int encode(int input, int red, int green, int blue) {
		int comb = random.nextInt(combinations.length);
		boolean[] b = combinations[comb];
		byte[] nibbles = getNibbles(input ^ (random.nextInt(256)-128));
		int i = 0;
		if (b[0]) {
			red = encodeBottomNibble(red, nibbles[i++]);
		} else {
			red = encodeBottomNibble(red, random2.nextInt(16));
		}
		if (b[1]) {
			green = encodeBottomNibble(green, nibbles[i++]);
		} else {
			green = encodeBottomNibble(green, random2.nextInt(16));
		}
		if (b[2]) {
			blue = encodeBottomNibble(blue, nibbles[i++]);
		}else {
			blue = encodeBottomNibble(blue, random2.nextInt(16));
		}
		
	
		return (red << 16) | (green << 8) | blue;
	}

	private byte[] getNibbles(int input) {
		input= input & 0xFF;
		byte[] b = new byte[] { (byte) ((input & 0xF0) >>> 4),
				(byte) (input & 0x0F) };
		return b;
	}

	private int encodeBottomNibble(int in, int toEncode) {
		if (in > 239) {
			in -= toEncode;
		} else if (in<16) {
			in += toEncode;
		} else {
			if (random2.nextBoolean()) {
				in -= toEncode;				
			} else {
				in += toEncode;				
			}
		}
		return in ;
	}

	@Override
	public int getNumberOfBytesEncodable(int width, int height) {
		return width * height;
	}

}
